package gengine

import (
	"fmt"
	"ginfwk/framework/core/gins"
	"ginfwk/infrastructure/facede"
	"ginfwk/infrastructure/helper/ghttp"
	"github.com/gin-gonic/gin"
	"net/http"
)

var (
	// anyMethods for RouterGroup Any method
	anyMethods = []string{
		http.MethodGet, http.MethodPost, http.MethodPut, http.MethodPatch,
		http.MethodHead, http.MethodOptions, http.MethodDelete, http.MethodConnect,
		http.MethodTrace,
	}
)

type GEngine struct {
	RouterGroup
	*gin.Engine
}

type RouterGroup struct {
	*gin.RouterGroup
	gEngine   *GEngine
	root      bool
	routerMap map[string]gins.RouterInfo
}

func (g *RouterGroup) GetRouteInfo(httpMethod, absolutePath string) (gins.RouterInfo, bool) {
	info, ok := g.routerMap[httpMethod+absolutePath]
	return info, ok
}

var _ gins.IEngine = (*GEngine)(nil)
var _ gins.IRoutes = (*RouterGroup)(nil)

func New() *GEngine {
	ginEngine := gin.New()
	gEngine := &GEngine{
		RouterGroup: RouterGroup{
			RouterGroup: &ginEngine.RouterGroup,
			gEngine:     nil,
			root:        true,
			routerMap:   make(map[string]gins.RouterInfo),
		},
		Engine: ginEngine,
	}
	gEngine.RouterGroup.gEngine = gEngine
	return gEngine
}

// ResponseHandler
// 返回数据写入处理
func (g *GEngine) ResponseHandler(ctx *gin.Context, v interface{}) {
	// json格式返回
	g.JsonResponseHandler(ctx, v)
}

func (g *GEngine) JsonResponseHandler(ctx *gin.Context, v interface{}) {
	if err, ok := v.(error); ok {
		ctx.JSON(
			http.StatusOK,
			ghttp.Fail(
				http.StatusInternalServerError,
				err.Error(),
			),
		)
	} else {
		ctx.JSON(http.StatusOK, v)
	}
}

// RequestHandler
// 请求函数封装转化
func (g *GEngine) RequestHandler(handlerFunc gins.HandlerFunc) gin.HandlerFunc {
	return func(ctx *gin.Context) {
		defer func() {
			// 异常处理
			if err := recover(); err != nil {
				// 记录异常日志
				ctxLogger := facede.CtxLogger(ctx.Request.Context())
				if errInfo, ok := err.(error); ok {
					ctxLogger.Errorf("handle http request interapted, err:" + errInfo.Error())
				} else {
					ctxLogger.Errorf("handle http request interapted, msg:" + fmt.Sprint(err))
				}
				g.ResponseHandler(ctx, err)
			}
		}()

		// 执行方法
		v := handlerFunc(ctx)

		// 写入返回
		g.ResponseHandler(ctx, v)
	}
}

// Use adds middleware to the group, see example code in GitHub.
func (e *GEngine) Use(middleware ...gin.HandlerFunc) gins.IRoutes {
	return e.RouterGroup.Use(middleware...)
}

// Use adds middleware to the group, see example code in GitHub.
func (group *RouterGroup) Use(middleware ...gin.HandlerFunc) gins.IRoutes {
	group.RouterGroup.Use(middleware...)
	return group.returnObj()
}

// Group creates a new router group. You should add all the routes that have common middlewares or the same path prefix.
// For example, all the routes that use a common middleware for authorization could be grouped.
func (group *RouterGroup) Group(relativePath string, handlers ...gin.HandlerFunc) gins.IRoutes {
	routerGroup := group.RouterGroup.Group(relativePath, handlers...)
	return &RouterGroup{
		RouterGroup: routerGroup,
		gEngine:     group.gEngine,
		root:        false,
	}
}

// Group creates a new router group. You should add all the routes that have common middlewares or the same path prefix.
// For example, all the routes that use a common middleware for authorization could be grouped.
func (e *GEngine) Group(relativePath string, handlers ...gin.HandlerFunc) gins.IRoutes {
	return e.RouterGroup.Group(relativePath, handlers...)
}

// Handle registers a new request handle and middleware with the given path and method.
// The last handler should be the real handler, the other ones should be middleware that can and should be shared among different routes.
// See the example code in GitHub.
//
// For GET, POST, PUT, PATCH and DELETE requests the respective shortcut
// functions can be used.
//
// This function is intended for bulk loading and to allow the usage of less
// frequently used, non-standardized or custom methods (e.g. for internal
// communication with a proxy).
func (group *RouterGroup) Handle(httpMethod, relativePath string, handlers ...gins.HandlerFunc) gins.IRoutes {
	var ginHandlers []gin.HandlerFunc
	for _, handler := range handlers {
		ginHandlers = append(ginHandlers, group.gEngine.RequestHandler(handler))
	}
	// register router for debug print.
	absolutePath := group.RouterGroup.Group(relativePath).BasePath()
	group.gEngine.RouterGroup.routerMap[httpMethod+absolutePath] = gins.RouterInfo{
		RelativePath: relativePath,
		AbsolutePath: absolutePath,
		Handlers:     handlers,
	}
	group.RouterGroup.Handle(httpMethod, relativePath, ginHandlers...)
	return group.returnObj()
}

// POST is a shortcut for router.Handle("POST", path, handlers).
func (group *RouterGroup) POST(relativePath string, handlers ...gins.HandlerFunc) gins.IRoutes {
	return group.Handle(http.MethodPost, relativePath, handlers...)
}

// GET is a shortcut for router.Handle("GET", path, handlers).
func (group *RouterGroup) GET(relativePath string, handlers ...gins.HandlerFunc) gins.IRoutes {
	return group.Handle(http.MethodGet, relativePath, handlers...)
}

// DELETE is a shortcut for router.Handle("DELETE", path, handlers).
func (group *RouterGroup) DELETE(relativePath string, handlers ...gins.HandlerFunc) gins.IRoutes {
	return group.Handle(http.MethodDelete, relativePath, handlers...)
}

// PATCH is a shortcut for router.Handle("PATCH", path, handlers).
func (group *RouterGroup) PATCH(relativePath string, handlers ...gins.HandlerFunc) gins.IRoutes {
	return group.Handle(http.MethodPatch, relativePath, handlers...)
}

// PUT is a shortcut for router.Handle("PUT", path, handlers).
func (group *RouterGroup) PUT(relativePath string, handlers ...gins.HandlerFunc) gins.IRoutes {
	return group.Handle(http.MethodPut, relativePath, handlers...)
}

// OPTIONS is a shortcut for router.Handle("OPTIONS", path, handlers).
func (group *RouterGroup) OPTIONS(relativePath string, handlers ...gins.HandlerFunc) gins.IRoutes {
	return group.Handle(http.MethodOptions, relativePath, handlers...)
}

// HEAD is a shortcut for router.Handle("HEAD", path, handlers).
func (group *RouterGroup) HEAD(relativePath string, handlers ...gins.HandlerFunc) gins.IRoutes {
	return group.Handle(http.MethodHead, relativePath, handlers...)
}

// Any registers a route that matches all the HTTP methods.
// GET, POST, PUT, PATCH, HEAD, OPTIONS, DELETE, CONNECT, TRACE.
func (group *RouterGroup) Any(relativePath string, handlers ...gins.HandlerFunc) gins.IRoutes {
	for _, method := range anyMethods {
		group.Handle(method, relativePath, handlers...)
	}

	return group.returnObj()
}

// Match registers a route that matches the specified methods that you declared.
func (group *RouterGroup) Match(methods []string, relativePath string, handlers ...gins.HandlerFunc) gins.IRoutes {
	for _, method := range methods {
		group.Handle(method, relativePath, handlers...)
	}
	return group.returnObj()
}

func (group *RouterGroup) returnObj() gins.IRoutes {
	if group.root {
		return group.gEngine
	}
	return group
}
